#include "common.h"

#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include "common_test.h"

/*
系统中有定义 offsetof，下面的这个原来广为流传的定义方式
作用：获取一个数据结构的成员，在这个数据结构中的偏移

将0地址强制转换为TYPE * 类型的指针，所以0指针指向的TYPE类型的成员的地址都是以相对于0地址的偏移，所以返回成员的地址，就是要获得的偏移。
    -> 优先级高于 取地址 ＆

在一些编译器中会对一些标准库的函数实现改用内置函数来代替，可以起到性能优化的作用。
因为执行这些函数调用会在编译时变为直接指令块的执行，而不会产生指令跳转、堆栈等相关的操作而引起的函数调用开销(有一些函数直接就有一条对应的机器指令来实现，如果改用普通函数调用势必性能大打折扣)。
不同的编译器对内置函数的支持不尽相同，而且对于是否用内置函数来实现标准库函数也没有统一的标准。比如对于GCC来说它所支持的内置函数都在GCC内置函数列表中被定义和声明，这些内置函数大部分也被LLVM编译器所支持。

__builtin_开头的符号其实是一些编译器内置的函数或者编译优化处理开关等，其作用类似于宏
*/
#define offsetof_a(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

/* 
此处的定义是网上流传的，与本项目中的原定义不同
分析语法：
第一步，首先定义一个临时的数据类型(通过typeof(((type *)0)->member)获得)与ptr相同的指针变量__mptr，然后用它来保存ptr的值。
第二步，用(char *)__mptr减去member在结构体中的偏移量，得到的值就是整个结构体变量的首地址(整个宏的返回值就是这个首地址)。
其中的语法难点就是如何得出成员相对结构体的偏移量。

#define container_of_a(ptr, type, member) ({			\
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
	(type *)( (char *)__mptr - offsetof(type,member) );})

没有编译通过    
————————————————
版权声明：本文为CSDN博主「晴天_QQ」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
原文链接：https://blog.csdn.net/caihaitao2000/article/details/80559967
*/

typedef struct person{
    /* char在32位系统中只占1个字节，因为内存对齐的原因，后面还有3个填充的空间 */
    char sex;
    char test;
    unsigned int age;
    char *name;
} person_p;

static person_p jack = {
    .name = "jack",
    .age = 18,
    .sex = 'm'
};

static void test_container_of(void){
    TEST_START_INFO;

    struct person *temp_jack;
    unsigned int *agex = &(jack.age); 
    
    /* 注意下面的语句中两个age是不同的 */
    temp_jack = container_of(agex, struct person, age);
    struct person temp = *temp_jack;
    
    /* 注意下面的声明是不需要 struct 关键字的 */
    person_p a = *temp_jack;
    
    assert('m' == a.sex);

    /* 结构中 . 与 -> 作用是不一样的 
    */
    assert(!strcmp("jack", temp.name));
    assert(18 == temp.age);
    assert('m' == temp.sex);

    assert(!strcmp("jack", temp_jack->name));
    assert(18 == temp_jack->age);
    assert('m' == temp_jack->sex);

    printf("jack's name is %s\n",temp_jack->name);
	printf("jack's age is %u\n",temp_jack->age);
	printf("jack's score is %c\n",temp_jack->sex);
}

static void test_offsetof_a(void){
    /* 对比两个不同的offsetof的宏的作用 */
    TEST_START_INFO;

    assert(0 == offsetof(struct person, sex));
    assert(1 == offsetof(struct person, test));
    assert(4 == offsetof(struct person, age));
    assert(8 == offsetof(struct person, name));

    assert(0 == offsetof_a(struct person, sex));
    assert(1 == offsetof(struct person, test));
    assert(4 == offsetof_a(struct person, age));
    assert(8 == offsetof_a(struct person, name));

    assert(1 == sizeof(jack.sex));

    printf("offsetof(struct person, sex) = %lu\n",offsetof_a(struct person, sex));
    printf("offsetof(struct person, test) = %lu\n",offsetof_a(struct person, test));
    printf("offsetof(struct person, age) = %lu\n",offsetof_a(struct person, age));
    printf("offsetof(struct person, name) = %lu\n",offsetof_a(struct person, name));  

    printf("sizeof(char)= %lu\n",sizeof(jack.sex));
}

static void test_common_min(void){
    TEST_START_INFO;
    int a = 10;
    int b = 14;
    int c = MIN(a,b);
    assert(a == c);

    c = 2 * MIN(a, b);
    printf("\n c= %d\n",c);
    //assert(a+1==c);
}

static void test_common_max(void){
    TEST_START_INFO;

    int a = 10;
    int b = 14;
    int c = MAX(a, b);
    assert(b == c);
}

static void test_common_clamp(void){
    TEST_START_INFO;
    
    int a = 10, b = 11, c = 12;
    int d = CLAMP(a, c, b);
    assert(b == d);
}

int main(int argc, char *argv[]) {
    (void) argc;
    (void) argv;

    test_offsetof_a();
    test_container_of();

    test_common_min();
    test_common_max();
    test_common_clamp();

    return 0;
};